home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Applications / Python 1.3.3 / Python 133 68K / Lib / macbinary.py < prev    next >
Text File  |  1996-05-20  |  8KB  |  341 lines

  1. """macbinary - Macintosh binhex compression/decompression
  2. easy interface:
  3. encode(inputfilename, outputfilename)
  4. decode(inputfilename, outputfilename)
  5. """
  6.  
  7. #
  8. # Jack Jansen, CWI, October 1995.
  9. # Adapted from binhex.py
  10. #
  11. # The module is supposed to be as compatible as possible. Especially the
  12. # easy interface should work "as expected" on any platform.
  13. # XXXX Note: currently, textfiles appear in mac-form on all platforms.
  14. # We seem to lack a simple character-translate in python.
  15. # (we should probably use ISO-Latin-1 on all but the mac platform).
  16. # XXXX The simeple routines are too simple: they expect to hold the complete
  17. # files in-core. Should be fixed.
  18. # XXXX It would be nice to handle AppleDouble format on unix (for servers serving
  19. # macs).
  20. import sys
  21. import os
  22. import struct
  23. import string
  24. import binascii
  25.  
  26. DEBUG=0
  27. if DEBUG:
  28.     testf=open('@macbinary.dbg.out', 'w')
  29.     
  30. Error = 'macbinary.Error'
  31.  
  32. # States (what have we written)
  33. [_DID_HEADER, _DID_DATA, _DID_RSRC] = range(3)
  34.  
  35. # Various constants
  36. REASONABLY_LARGE=32768    # Minimal amount we pass the rle-coder
  37. LINELEN=48        # What we pass to hqx-coder at once
  38.             # *NOTE* Must be divisible by 3!
  39. RUNCHAR=chr(0x90)    # run-length introducer
  40.  
  41. #
  42. # The code is currently byte-order dependent
  43. if struct.pack('i', 0177) != '\0\0\0\177':
  44.     raise ImportError, 'Module binhex is big-endian only'
  45.  
  46. #
  47. # Workarounds for non-mac machines.
  48. if os.name == 'mac':
  49.     import macfs
  50.     import MacOS
  51.     try:
  52.         openrf = MacOS.openrf
  53.     except AttributeError:
  54.         # Backward compatability
  55.         openrf = open
  56.  
  57.     def FInfo():
  58.         return macfs.FInfo()
  59.  
  60.     def getfileinfo(name):
  61.         finfo = macfs.FSSpec(name).GetFInfo()
  62.         dir, file = os.path.split(name)
  63.         # XXXX Get resource/data sizes
  64.         fp = open(name, 'rb')
  65.         fp.seek(0, 2)
  66.         dlen = fp.tell()
  67.         fp = openrf(name, '*rb')
  68.         fp.seek(0, 2)
  69.         rlen = fp.tell()
  70.         return file, finfo, dlen, rlen
  71.  
  72.     def openrsrc(name, *mode):
  73.         if mode:
  74.             mode = mode[0]
  75.         else:
  76.             mode = 'rb'
  77.         mode = '*' + mode
  78.         return openrf(name, mode)
  79.  
  80. else:
  81.     #
  82.     # Glue code for non-macintosh useage
  83.     #
  84.     import regsub
  85.     
  86.     class FInfo:
  87.         def __init__(self):
  88.             self.Type = '????'
  89.             self.Creator = '????'
  90.             self.Flags = 0
  91.  
  92.     def getfileinfo(name):
  93.         finfo = FInfo()
  94.         # Quick check for textfile
  95.         fp = open(name)
  96.         data = open(name).read(256)
  97.         for c in data:
  98.             if not c in string.whitespace and (c<' ' or ord(c) > 0177):
  99.             break
  100.         else:
  101.             finfo.Type = 'TEXT'
  102.         fp.seek(0, 2)
  103.         dsize = fp.tell()
  104.         fp.close()
  105.         dir, file = os.path.split(name)
  106.         file = regsub.sub(':', '-', file)
  107.         return file, finfo, dsize, 0
  108.  
  109.     class openrsrc:
  110.         def __init__(self, *args):
  111.             pass
  112.     
  113.         def read(self, *args):
  114.             return ''
  115.     
  116.         def write(self, *args):
  117.             pass
  118.             
  119.         def close(self):
  120.             pass
  121.     
  122.  
  123. class Encoder:
  124.     def __init__(self, (name, finfo, dlen, rlen), ofp):
  125.         if type(ofp) == type(''):
  126.             ofname = ofp
  127.             ofp = open(ofname, 'w')
  128.             if os.name == 'mac':
  129.                 fss = macfs.FSSpec(ofname)
  130.                 fss.SetCreatorType('BnHq', 'TEXT')
  131.         self.ofp = ofp
  132.         if finfo == None:
  133.             finfo = FInfo()
  134.         self.dlen = dlen
  135.         self.rlen = rlen
  136.         self.dtrailer = (dlen + 127) & 127
  137.         self.rtrailer = (rlen + 127) & 127
  138.         self._writeinfo(name, finfo)
  139.         self.state = _DID_HEADER
  140.  
  141.     def _writeinfo(self, name, finfo):
  142.         if DEBUG:
  143.             print 'binhex info:', name, finfo.Type, finfo.Creator, self.dlen, self.rlen
  144.         name = name
  145.         nl = len(name)
  146.         if nl > 63:
  147.             raise Error, 'Filename too long'
  148.         d = '\0' + chr(nl) + name + '\0'*(63-nl)
  149.         d2 = finfo.Type + finfo.Creator + chr(finfo.Flags&0xff) + '\0\0\0\0\0\0\0' + '\0\0'
  150.         d3 = struct.pack('iiii', self.dlen, self.rlen, 0, 0)
  151.         d4 = '\0\0' + chr((finfo.Flags>>8) & 0xff)
  152.         info = d + d2 + d3 + d4
  153.         info = info + '\0'*(128-len(info))
  154.         self._write(info)
  155.  
  156.     def _write(self, data):
  157.         self.ofp.write(data)
  158.  
  159.     def write(self, data):
  160.         if self.state != _DID_HEADER:
  161.             raise Error, 'Writing data at the wrong time'
  162.         self.dlen = self.dlen - len(data)
  163.         self._write(data)
  164.  
  165.     def close_data(self):
  166.         if self.dlen <> 0:
  167.             raise Error, 'Incorrect data size, diff='+`self.rlen`
  168.         self._write('\0'*self.dtrailer)
  169.         self.state = _DID_DATA
  170.  
  171.     def write_rsrc(self, data):
  172.         if self.state < _DID_DATA:
  173.             self.close_data()
  174.         if self.state != _DID_DATA:
  175.             raise Error, 'Writing resource data at the wrong time'
  176.         self.rlen = self.rlen - len(data)
  177.         self._write(data)
  178.  
  179.     def close(self):
  180.         if self.state < _DID_DATA:
  181.             self.close_data()
  182.         if self.state != _DID_DATA:
  183.             raise Error, 'Close at the wrong time'
  184.         if self.rlen <> 0:
  185.             raise Error, "Incorrect resource-datasize, diff="+`self.rlen`
  186.         self._write('\0'*self.rtrailer)
  187.         self.ofp.close()
  188.         self.state = None
  189.     
  190. def encode(inp, out):
  191.     """(infilename, outfilename) - Create MacBinary-encoded copy of a file"""
  192.     finfo = getfileinfo(inp)
  193.     ofp = Encoder(finfo, out)
  194.     
  195.     ifp = open(inp, 'rb')
  196.     # XXXX Do textfile translation on non-mac systems
  197.     d = ifp.read()
  198.     ofp.write(d)
  199.     ofp.close_data()
  200.     ifp.close()
  201.  
  202.     ifp = openrsrc(inp, 'rb')
  203.     d = ifp.read()
  204.     ofp.write_rsrc(d)
  205.     ofp.close()
  206.     ifp.close()    
  207.  
  208. class Decoder:
  209.     def __init__(self, ifp):
  210.         if type(ifp) == type(''):
  211.             ifp = open(ifp)
  212.         self.ifp = ifp
  213.         self._readheader()
  214.         
  215.     def _read(self, length):
  216.         data = ''
  217.         while lenght:
  218.             newdata = self.ifp.read(length)
  219.             if not newdata:
  220.                 raise Error, 'Premature end-of-file'
  221.             data = data + newdata
  222.             length = length - len(newdata)
  223.         return data
  224.  
  225.     def _readheader(self):
  226.         first = self._read(1)
  227.         if first <> '\0':
  228.             raise Error, 'Not a MacBinary file'
  229.         len = self._read(1)
  230.         fname = self._read(63)
  231.         fname = fname[:len]
  232.         type = self._read(4)
  233.         creator = self._read(4)
  234.         flagsXXXXX Here I left off....
  235.         self._checkcrc()
  236.         
  237.         type = rest[1:5]
  238.         creator = rest[5:9]
  239.         flags = struct.unpack('h', rest[9:11])[0]
  240.         self.dlen = struct.unpack('l', rest[11:15])[0]
  241.         self.rlen = struct.unpack('l', rest[15:19])[0]
  242.         
  243.         if DEBUG:
  244.             print 'DATA, RLEN', self.dlen, self.rlen
  245.         
  246.         self.FName = fname
  247.         self.FInfo = FInfo()
  248.         self.FInfo.Creator = creator
  249.         self.FInfo.Type = type
  250.         self.FInfo.Flags = flags
  251.         
  252.         self.state = _DID_HEADER
  253.         
  254.     def read(self, *n):
  255.         if self.state != _DID_HEADER:
  256.             raise Error, 'Read data at wrong time'
  257.         if n:
  258.             n = n[0]
  259.             n = min(n, self.dlen)
  260.         else:
  261.             n = self.dlen
  262.         self.dlen = self.dlen - n
  263.         return self._read(n)
  264.         
  265.     def close_data(self):
  266.         if self.state != _DID_HEADER:
  267.             raise Error, 'close_data at wrong time'
  268.         if self.dlen:
  269.             dummy = self._read(self.dlen)
  270.         self._checkcrc()
  271.         self.state = _DID_DATA
  272.         
  273.     def read_rsrc(self, *n):
  274.         if self.state == _DID_HEADER:
  275.             self.close_data()
  276.         if self.state != _DID_DATA:
  277.             raise Error, 'Read resource data at wrong time'
  278.         if n:
  279.             n = n[0]
  280.             n = min(n, self.rlen)
  281.         else:
  282.             n = self.rlen
  283.         self.rlen = self.rlen - n
  284.         return self._read(n)
  285.         
  286.     def close(self):
  287.         if self.rlen:
  288.             dummy = self.read_rsrc(self.rlen)
  289.         self._checkcrc()
  290.         self.state = _DID_RSRC
  291.         self.ifp.close()
  292.         
  293. def hexbin(inp, out):
  294.     """(infilename, outfilename) - Decode binhexed file"""
  295.     ifp = HexBin(inp)
  296.     finfo = ifp.FInfo
  297.     if not out:
  298.         out = ifp.FName
  299.     if os.name == 'mac':
  300.         ofss = macfs.FSSpec(out)
  301.         out = ofss.as_pathname()
  302.  
  303.     ofp = open(out, 'wb')
  304.     # XXXX Do translation on non-mac systems
  305.     d = ifp.read()
  306.     ofp.write(d)
  307.     ofp.close()
  308.     ifp.close_data()
  309.     
  310.     d = ifp.read_rsrc()
  311.     if d:
  312.         ofp = openrsrc(out, 'wb')
  313.         ofp.write(d)
  314.         ofp.close()
  315.  
  316.     if os.name == 'mac':
  317.         nfinfo = ofss.GetFInfo()
  318.         nfinfo.Creator = finfo.Creator
  319.         nfinfo.Type = finfo.Type
  320.         nfinfo.Flags = finfo.Flags
  321.         ofss.SetFInfo(nfinfo)
  322.     
  323.     ifp.close()
  324.  
  325. def _test():
  326.     if os.name == 'mac':
  327.         fss, ok = macfs.PromptGetFile('File to convert:')
  328.         if not ok:
  329.             sys.exit(0)
  330.         fname = fss.as_pathname()
  331.     else:
  332.         fname = sys.argv[1]
  333.     #binhex(fname, fname+'.hqx')
  334.     #hexbin(fname+'.hqx', fname+'.viahqx')
  335.     hexbin(fname, fname+'.unpacked')
  336.     sys.exit(1)
  337.     
  338. if __name__ == '__main__':
  339.     _test()
  340.         
  341.